import type { ElementSelectors } from '../../lib/abstract'
import type { ComponentStyleIdType } from '../../types/interfaces'
import type { ShortcutInputProps } from './types'
import { UIComponent } from '../../lib/abstract'
import { ComponentStyleId } from '../../types/constants'
import { TextInput } from '../text_input'
import { styles } from './styles'
import { DEFAULT_SHORTCUT_INPUT_PROPS } from './types'

export class ShortcutInput extends UIComponent<ShortcutInputProps> {
  id: string = 'shortcut-input'
  readonly description: string = '快捷键输入框'
  readonly styleId: string = ComponentStyleId[this.name]
  private currentKeys: string[] = []
  private isSettingValue = false
  private isRecording = false

  get name(): ComponentStyleIdType {
    return 'Shortcuts'
  }

  get className(): string {
    return 'custom-shortcut-input'
  }

  constructor(props: ShortcutInputProps) {
    super({
      ...DEFAULT_SHORTCUT_INPUT_PROPS,
      ...props,
    })

    // 注册样式
    this.registerStyles()
  }

  ELEMENT_SELECTORS: ElementSelectors = {
    input: { selector: `.shortcut-input`, dynamic: false, unique: false },
  }

  /**
   * 设置选择值
   */
  setValue(value: string): void {
    if (this.isSettingValue)
      return

    this.isSettingValue = true

    try {
      const $input = this.getElement('input')

      if (!$input || !$input.length)
        return

      $input.val(value)
      this.props.onChange?.(value)
    }
    finally {
      this.isSettingValue = false
    }
  }

  /**
   * 渲染输入框
   */
  render(): string {
    const { label, labelTextPosition } = this.props

    return `
      <div class="${this.className}">
        ${label ? `<label style="text-align: ${labelTextPosition} !important;">${label}</label>` : ''}
        ${this.renderTextInput()}
      </div>
    `
  }

  /**
   * 渲染文本输入框
   * @returns {string} 文本输入框 HTML
   */
  private renderTextInput(): string {
    const { id, shortcut, placeholder, activePlaceholder } = this.props

    return new TextInput({
      id: `${id}-input`,
      type: 'text',
      inputClassName: 'shortcut-input',
      placeholder: this.isRecording ? activePlaceholder : placeholder,
      value: this.formatShortcut(shortcut),
    }).render()
  }

  private formatShortcut(shortcut: string): string {
    return shortcut.split('+').map((part) => {
      if (['ctrl', 'alt', 'shift', 'meta'].includes(part.toLowerCase())) {
        return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()
      }
      return part.toUpperCase()
    }).join('+')
  }

  bindEvents(): void {
    const $input = this.getElement('input')

    if (!$input || !$input.length)
      return

    $input
      .on('focus', () => this.startRecording())
      .on('blur', () => this.stopRecording())
      .on('keydown', (e) => {
        e.preventDefault() // 阻止默认输入行为
        e.stopPropagation()
      })

    // 【注意】第三个参数 true 表示在捕获阶段处理事件（从外向内），jQuery 的 .on() 默认在冒泡阶段处理事件
    document.addEventListener('keydown', this.handleDocumentKeyDown.bind(this), true)

    this.props.onInit?.(this)
  }

  private startRecording(): void {
    this.isRecording = true
    this.currentKeys = []

    this.getElement('input')
      ?.addClass('recording')
      .attr('placeholder', this.props.activePlaceholder || '')
  }

  private stopRecording(): void {
    if (!this.isRecording)
      return

    this.isRecording = false
    const $input = this.getElement('input')

    if (this.currentKeys.length > 0) {
      const shortcut = this.currentKeys.join('+')
      $input?.val(this.formatShortcut(shortcut))
      this.props.onChange?.(shortcut)
    }
    else {
      // 恢复历史快捷键
      $input?.val(this.formatShortcut(this.props.shortcut))
    }

    $input
      ?.removeClass('recording')
      .attr('placeholder', this.props.placeholder || '')
  }

  private handleDocumentKeyDown(e: KeyboardEvent): void {
    if (!this.isRecording)
      return

    // 转换为jQuery事件对象
    const $event = $.Event('keydown', {
      key: e.key,
      ctrlKey: e.ctrlKey,
      altKey: e.altKey,
      shiftKey: e.shiftKey,
      metaKey: e.metaKey,
    })

    this.handleKeyPress($event as any)
  }

  private handleKeyPress(e: JQuery.KeyDownEvent): void {
    e.preventDefault()
    e.stopPropagation()

    // 处理特殊键
    if (e.key === 'Escape') {
      this.stopRecording()
      return
    }

    if (e.key === 'Enter' && this.currentKeys.length > 0) {
      this.stopRecording()
      return
    }

    // 处理Delete键（清空快捷键）
    if (e.key === 'Delete') {
      this.currentKeys = []
      this.getElement('input')?.val('')
      this.props.onChange?.('') // 传递空字符串表示清空
      return
    }

    // 重置当前按键（只保留最新组合）
    this.currentKeys = []

    // 获取标准化键名
    const key = this.getKeyName(e)

    if (!key)
      return

    // 更新当前按键
    if (!this.currentKeys.includes(key)) {
      this.currentKeys.push(key)
      this.getElement('input')?.val(this.currentKeys.join('+'))
    }
  }

  private getKeyName(e: JQuery.KeyDownEvent): string | null {
    // 忽略纯修饰键按下事件
    if (['Control', 'Alt', 'Shift', 'Meta'].includes(e.key)) {
      return null
    }

    const keys: string[] = []
    let hasModifier = false

    if (e.ctrlKey) {
      keys.push('Ctrl')
      hasModifier = true
    }
    if (e.altKey) {
      keys.push('Alt')
      hasModifier = true
    }
    if (e.shiftKey) {
      keys.push('Shift')
      hasModifier = true
    }
    if (e.metaKey) {
      keys.push('Meta')
      hasModifier = true
    }

    // 必须至少有一个修饰键
    if (!hasModifier) {
      return null
    }

    // 处理有效键位
    if (e.key.length === 1 || e.key === ' ') {
      keys.push(e.key.toUpperCase())
    }
    else if ([
      'Enter',
      'Escape',
      'Tab',
      'ArrowUp',
      'ArrowDown',
      'ArrowLeft',
      'ArrowRight',
      'Home',
      'End',
      'PageUp',
      'PageDown',
      'F1',
      'F2',
      'F3',
      'F4',
      'F5',
      'F6',
      'F7',
      'F8',
      'F9',
      'F10',
      'F11',
      'F12',
    ].includes(e.key)) {
      keys.push(e.key)
    }
    else {
      return null
    }

    return keys.join('+')
  }

  getStyles(): string {
    return styles(this.className)
  }

  reset(): void {
    super.reset()
  }
}
